using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Net;


namespace TankWars
{
    public class TankManager
    {
        /// <summary>
        /// Holds all players and tanks
        /// </summary>
        public Dictionary<Player, Tank> dicTanks = new Dictionary<Player, Tank>();
        private Dictionary<Type, Texture2D> dicShotTextures = new Dictionary<Type, Texture2D>();
        private Dictionary<Type, Texture2D> dicShotIcons = new Dictionary<Type, Texture2D>();
        private Dictionary<Type, string> dicShotSpriteNames = new Dictionary<Type, string>();

        private string strAnimationXMLFile = "";
        private Texture2D tankSprite;
        private Texture2D turretSprite;
        private SpriteBatch spriteBatch;
        
        private NetworkSession networkSession;

        public Rules gameRules;

        /// <summary>
        /// Creates a new tankmanager
        /// </summary>
        /// <param name="TankSprite"></param>
        /// <param name="spriteBatch"></param>
        public TankManager(Texture2D TankSprite, Texture2D TankTurretSprite, Dictionary<Type, Texture2D> shotTextures, Dictionary<Type, Texture2D> shotIcons, Dictionary<Type, string> shotSpriteNames, string strAnimationXMLFile, SpriteBatch spriteBatch)
        {
            this.tankSprite = TankSprite;
            this.turretSprite = TankTurretSprite;

            this.dicShotTextures = shotTextures;
            this.dicShotIcons = shotIcons;
            this.dicShotSpriteNames = shotSpriteNames;

            this.strAnimationXMLFile = strAnimationXMLFile;

            this.spriteBatch = spriteBatch;
        }
        
        public void setNetwork(NetworkSession net)
        {
            this.networkSession = net;
        }

        /// <summary>
        /// Creates a new tank for the given player
        /// </summary>
        /// <param name="player">Player the tank belongs to</param>
        public void NewTank(Player player)
        {
            Tank t = new Tank(this.tankSprite, this.turretSprite ,player, this.dicShotTextures, this.dicShotIcons, this.dicShotSpriteNames, this.strAnimationXMLFile);
            this.dicTanks.Add(player, t);
            player.setTank(t);
        }

        /// <summary>
        /// Creates a new tank for the given player with the given color
        /// </summary>
        /// <param name="player">Player the tank belongs to</param>
        /// <param name="tankColor">Color of the tank</param>
        public void NewTank(Player player, Color tankColor)
        {
            Tank t = new Tank(this.tankSprite, this.turretSprite, tankColor, player, this.dicShotTextures, this.dicShotIcons, this.dicShotSpriteNames, this.strAnimationXMLFile);
            this.dicTanks.Add(player, t);
            player.setTank(t);
        }

        /// <summary>
        /// Gets the tank that belongs to the local gamer
        /// </summary>
        public Tank getTank(LocalNetworkGamer gamer)
        {
                // Look up what player is associated with the gamer.
                Player player = gamer.Tag as Player;
                return player.Tank;
            
        }

        /// <summary>
        /// Draws all tanks
        /// </summary>
        /// <param name="map">Game map</param>
        public void Draw(Map map)
        {
            this.spriteBatch.Begin();

            #region Draw Tanks
            if (networkSession != null)
            {
                foreach (NetworkGamer gamer in networkSession.AllGamers)
                {
                    // Look up what player is associated with the gamer.
                    Player player = gamer.Tag as Player;

                    if ((player.Tank.IsRespawning)&&(!player.Tank.IsAnimating))
                    {
                        //Don't draw tank.
                    }
                    else
                    {
                        player.Tank.DrawTank(this.spriteBatch, map);
                    }
                }
            }
            #endregion

            #region Draw Tank shots
            if (networkSession != null)
            {
                foreach (NetworkGamer gamer in networkSession.AllGamers)
                {
                    // Look up what player is associated with the gamer.
                    Player player = gamer.Tag as Player;

                    player.Tank.WeaponManager.DrawPrimary(this.spriteBatch, map);
                }
            }
            //Draws all the non player tank shots in the dictionary.
            foreach (KeyValuePair<Player, Tank> CurrentPlayerTank in this.dicTanks)
            {
                Tank t = CurrentPlayerTank.Value;
                t.WeaponManager.DrawPrimary(this.spriteBatch, map);
            }

            if (networkSession != null)
            {
                foreach (NetworkGamer gamer in networkSession.AllGamers)
                {
                    // Look up what player is associated with the gamer.
                    Player player = gamer.Tag as Player;

                    player.Tank.WeaponManager.DrawSecondary(this.spriteBatch, map);
                }
            }

            //Draws all the non player tank shots in the dictionary.
            foreach (KeyValuePair<Player, Tank> CurrentPlayerTank in this.dicTanks)
            {
                Tank t = CurrentPlayerTank.Value;
                t.WeaponManager.DrawSecondary(this.spriteBatch, map);
            }
            #endregion

            this.spriteBatch.End();
        }

        /// <summary>
        /// Updates the position of all tanks
        /// </summary>
        /// <param name="gameTime"></param>
        public void Update(GameTime gameTime, Microsoft.Xna.Framework.Input.MouseState mousestate, Map map, GameField field, Player mainPlayer)
        {
            foreach (NetworkGamer gamer in networkSession.AllGamers)
            {
                // Look up what player is associated with the gamer.
                Player player = gamer.Tag as Player;

                player.Tank.UpdatePosition(gameTime, mousestate, map,mainPlayer);
            }

            //Now check for collisions
            checkCollisions(gameTime, field);
        }

        public void checkCollisions(GameTime gameTime, GameField field)
        {
            checkTankCollisions(gameTime, field);
            checkFieldCollisions(field, gameTime);
        }

        public void checkTankCollisions(GameTime gameTime, GameField field)
        {
            #region Tank on Tank Collisions
            //Tank on Tank collisions
            for (int i = 0; i < networkSession.AllGamers.Count; i++)
            {
                Player p1 = networkSession.AllGamers[i].Tag as Player;
                if (!p1.Tank.IsRespawning)
                {
                    Vector2[] shape1 = p1.Tank.getPoints();

                    for (int j = i + 1; j < networkSession.AllGamers.Count; j++)
                    {
                        Player p2 = networkSession.AllGamers[j].Tag as Player;
                        if (!p2.Tank.IsRespawning)
                        {
                            if (MovableSprite.shapeCollision(shape1, p2.Tank.getPoints()))
                            {
                                p1.Tank.handleXCollision();
                                p1.Tank.handleYCollision();
                                p2.Tank.handleXCollision();
                                p2.Tank.handleYCollision();
                            }
                        }//If: Tank isn't respawning
                    }//For: inner
                }//IF: Tank isn't respawning
            }//For: outter
            #endregion

            #region Bullet on Tank Collisions
            foreach (NetworkGamer gamer in this.networkSession.AllGamers)
            {
                Player currentPlayer = gamer.Tag as Player;
                Tank currentTank = currentPlayer.Tank;

                    foreach (NetworkGamer checkGamer in this.networkSession.AllGamers)
                    {
                        Player checkPlayer = checkGamer.Tag as Player;
                        Tank checkTank = checkPlayer.Tank;

                        if (!checkTank.IsRespawning)
                        {
                            List<Weapons.Shot> lstShotsHit = currentTank.WeaponManager.CheckForShotCollisions(checkTank);

                            foreach (Weapons.Shot shotHit in lstShotsHit)
                            {
                                if (!shotHit.bIsExploded)
                                {
                                    //If the hit destroyed the check tank
                                    if (!checkTank.Hit(shotHit, gameTime))
                                    {
                                        if (currentTank != checkTank)
                                        currentTank.TankPlayer.Kills++;
                                        break;
                                    }
                                }
                            }//Foreach: shot
                        }//If: !checkTank.IsRespawning
                    }//Foreach: checkGamer
            }//Foreach: gamer
            #endregion

            #region Update Respawn Timers
            foreach (NetworkGamer gamer in this.networkSession.AllGamers)
            {
                Player currentPlayer = gamer.Tag as Player;
                Tank currentTank = currentPlayer.Tank;

                if ((currentTank.IsRespawning) && ((gameTime.TotalGameTime.Seconds - currentTank.lastTimeDead > 5)||(currentTank.lastTimeDead > gameTime.TotalGameTime.Seconds)))
                {
                    if (this.gameRules.CanTankRespawn(currentTank))
                    {
                        currentTank.vPosition = field.getSpawnPoint();
                        currentTank.ResetHealth();
                    }
               }
            }//Foreach:gamer
            #endregion

            /////
        }

        /// <summary>
        /// Checks for collisions between a tank and the game field.
        /// </summary>
        public void checkFieldCollisions(GameField field, GameTime gameTime)
        {
                foreach (NetworkGamer gamer in networkSession.AllGamers)
                {
                    // Look up what player is associated with the gamer.
                    Player player = gamer.Tag as Player;
                    Tank tank = player.Tank;

                    #region Tank Off Screen
                    //First check if the tank is off the screen.
                    if (tank.vPosition.X > field.getMapWidth())
                        tank.vPosition.X = 0;
                    if (tank.vPosition.X < 0)
                        tank.vPosition.X = field.getMapWidth();
                    if (tank.vPosition.Y > field.getMapHeight())
                        tank.vPosition.Y = 0;
                    if (tank.vPosition.Y < 0)
                        tank.vPosition.Y = field.getMapHeight();
                    #endregion

                    #region Tank on Field Collision
                    //Convert the tank's coordinates into field matrix coordinates.
                    int fieldX = (int)(tank.vPosition.X/field.blockSize);
                    int fieldY = (int)(tank.vPosition.Y/field.blockSize);

                    //Reset the level the tank is on before checking.
                    tank.highGround = false;

                    //Cycle through the field positions that should be checked.
                    int tankRadius = ((tank.tankWidth / 2)/field.blockSize)+1;
                    for (int x = fieldX - tankRadius; x <= fieldX + tankRadius; x++)
                    for (int y = fieldY - tankRadius; y <= fieldY + tankRadius; y++)
                    {
                        //Get cell
                        int cell = field.getCell(x, y);

                        //If the field has a defined property.
                        if (cell >0)
                        {
                            
                            //If the tank collided with a block.
                            if (MovableSprite.shapeCollision(tank.getPoints(), field.getPoints(x, y)))
                            {
                                //If there is a block at the location.
                                if ((cell >= 1) && (cell <= 3))
                                {
                                    tank.handleYCollision();
                                    tank.handleXCollision();
                                }
                                else//Currently high ground is the only other object.
                                {
                                    tank.highGround = true;
                                }
                            }//If: tank Collided.
                        }//If: field Occupied
                    }//For:cycle through all field positions.
                    #endregion

                    #region Bullet on Field Collision
                    tank.WeaponManager.checkFieldCollisions(field, gameTime);
                    #endregion

            }//foreach: gamer in networkSession
        }//Method: Check field collisions

        /*
        /// <summary>
        /// Method checks to see if two polygons have collided.
        /// </summary>
        /// <param name="shape1"> A linked list of points that creates a shape</param>
        /// <param name="shape2">The second shape that is being checked for collisions.</param>
        /// <returns>Returns bool true if they have collided.</returns>
        public bool shapeCollision(Vector2[] shape1, Vector2[] shape2)
        {
            //Go through all the points in shape1.
            for (int p1 = 0; p1 < shape1.Length; p1++)
            {
                //Go through all the points in shape2
                for (int p2 = 0; p2 < shape2.Length; p2++)
                {
                    //Connecting points will be the next point in the shape.
                    int cp1 = p1 + 1;
                    int cp2 = p2 + 1;

                    //If p1 is at the last point, the connect point will be 0
                    if (p1 == shape1.Length - 1)
                        cp1 = 0;
                    
                    //If at last point in shape2.
                    if (p2 == shape2.Length - 1)
                        cp2 = 0;

                    if (lineIntersect(shape1[p1], shape1[cp1], shape2[p2], shape2[cp2]))
                        return true;
                }//For: shape2 points
            }//For: shape1 points

            return false;
        }

        /// <summary>
        /// Determines the point of intersection between two line segments, 
        /// as defined by four points.
        /// </summary>
        /// <param name="a">The first point on the first line segment.</param>
        /// <param name="b">The second point on the first line segment.</param>
        /// <param name="c">The first point on the second line segment.</param>
        /// <param name="d">The second point on the second line segment.</param>
        /// <param name="point">The output value with the interesection, if any.</param>
        /// <remarks>The output parameter "point" is only valid
        /// when the return value is true.</remarks>
        /// <returns>True if intersecting, false otherwise.</returns>
        public static bool lineIntersect(Vector2 a, Vector2 b, Vector2 c, Vector2 d)//, out Vector2 point)
        {
            //point = Vector2.Zero;
          
            double r, s;
            double denominator = (b.X - a.X) * (d.Y - c.Y) - (b.Y - a.Y) * (d.X - c.X);

            // If the denominator in above is zero, AB & CD are colinear
            if (denominator == 0)
            {
                return false;
            }

            double numeratorR = (a.Y - c.Y) * (d.X - c.X) - (a.X - c.X) * (d.Y - c.Y);
            r = numeratorR / denominator;

            double numeratorS = (a.Y - c.Y) * (b.X - a.X) - (a.X - c.X) * (b.Y - a.Y);
            s = numeratorS / denominator;

            // non-intersecting
            if (r < 0 || r > 1 || s < 0 || s > 1)
            {
                return false;
            }

            // find intersection point
            //point.X = (float)(a.X + (r * (b.X - a.X)));
           // point.Y = (float)(a.Y + (r * (b.Y - a.Y)));

            return true;
        }
         */
    }
}